/**
YASTACS universum data generator.

Authors: Kalman Kiss, kiskami@freemail.hu

Copyright:

Bugs:

Date: 

License: GNU General Public License

YASTACS game
Copyright (C) 2012 Kalman Kiss, kiskami@freemail.hu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Contact author at: kiskami@freemail.hu or
Hungary, 8900 Zalaegerszeg
Kakukkfu u 4.
*/
module ugen.UniversumGenerator;


import std.conv;
import std.math;
import std.stdio;
import std.file;

import ugen.UniversumData;
import ugen.RandomSource;
import Common;

class UniversumGenerator {

	///
	this(in string seed, in uint startoffset_=0) {
		rs = new RandomSource(seed,startoffset_, MAINRND_MAX);
		ud = new Universum(MAX_XSIZE, MAX_YSIZE, MAX_ZSIZE);
	}

	void loadPlanetNameDB(in string filename) {

//		planetname_list = cast(char[]) read(filename);

		auto f = File(filename, "r");
		string buf;
		while ((buf = f.readln()) != null) {
			planetname_list ~= buf;
		}
	}

	void setProgressDelegate(progressdelegate d) {
		pd = d;
	}

	void setLogDelegate(logdelegate d) {
		ld = d;
	}

	/*******************************************************/
	void gen() {

		auto maxsectors = MAX_XSIZE * MAX_YSIZE * MAX_ZSIZE * UNVERSUM_DENSITY;
		debug write ("Generating ", maxsectors, " sectors:");
		updatelog("Generating " ~ to!string(maxsectors) ~ " sectors: ");
		for(auto i = 0 ; i < maxsectors; ++i) {
			auto x = rs.getNextMainRnd(MAX_XSIZE),
				y = rs.getNextMainRnd(MAX_YSIZE),
				z = rs.getNextMainRnd(MAX_ZSIZE);

			Universum.Species species = getaSpecies();
			debug write(cast(uint)species, "-");
			debug updatelog(to!string(cast(uint)species) ~ "-");
			Sector s = ud.addSector(x, y, z, genSectorName(x, y, z, species), species);
			if(s !is null) {
				// bh in sector?
				auto bhn = rs.getNextMainRnd(MAX_BLACKHOLE_PER_SECTOR+1);
				auto max_cloud_count = 0;
				if(bhn>0 && rs.checkProbability(BLACKHOLE_PROBABILITY)) {
					s.addBlackHole(genBlackHole(x, y, z));
					max_cloud_count = MAX_ASTB_OR_CLO_PER_SECTOR2;
					debug write("b");
					debug updatelog("b");
				} else {
					// no bh - here comes the solar systems
					max_cloud_count = MAX_ASTB_OR_CLO_PER_SECTOR;
					auto ssn = rs.getNextMainRnd(MIN_SOLSYSTEMS_PER_SECTOR
											  ,MAX_SOLSYSTEMS_PER_SECTOR+1);
					for(auto j = 0; j < ssn; ++j) {
						s.addSolSys(genSolSys(x, y, z));
						debug write("S");
						debug updatelog("S");
					}
				}

				// clouds and asteroid belts
				auto cloud_count = rs.getNextMainRnd(max_cloud_count+1);
				if (cloud_count>0 && rs.checkProbability(ASTB_OR_CLO_PROBABILITY))
					for(auto j = 0; j < cloud_count; ++j) {
						if(rs.checkProbability(ASTEROID_BELT_PROBABILITY)) {
							s.addAsteroidBelt(genAsteroidBelt(x,y,z,j));
							debug write("a");
							debug updatelog("a");
						} else {
							s.addCloud(genCloud(x,y,z,j));
							debug write("c");
							debug updatelog("c");
						}
					}
			}
			debug write(". ");
			debug updatelog(". ");
			updateprogress(i*1.0/maxsectors);
		}
		debug writeln();
//		updatelog("\n");
	}

	@property universum() { return ud; }

private:

	/// fo randomgenerator ertektartomany defmaximum
	immutable uint MAINRND_MAX = 1000;

	/// max universum meret X iranyban
	immutable uint MAX_XSIZE = 102;
	/// max universum meret Y iranyban
	immutable uint MAX_YSIZE = 102;
	/// max universum meret Z iranyban
	immutable uint MAX_ZSIZE = 102;

	immutable float UNVERSUM_DENSITY = 0.001;

	/// hány solsys van egy szektorban
	immutable uint MIN_SOLSYSTEMS_PER_SECTOR = 1;
	immutable uint MAX_SOLSYSTEMS_PER_SECTOR = 5;

	/// hány asteroid belt vagy cloud van egy szektorban
	immutable uint MIN_ASTB_OR_CLO_PER_SECTOR = 0;
	immutable uint MAX_ASTB_OR_CLO_PER_SECTOR = 2;
	immutable uint MAX_ASTB_OR_CLO_PER_SECTOR2 = 1;
	immutable float ASTB_OR_CLO_PROBABILITY = 0.3;

	immutable uint MAX_BLACKHOLE_PER_SECTOR = 1;
	immutable float BLACKHOLE_PROBABILITY = 0.1;

	/// hány csillag lehet egy naprendszerben
	immutable uint MIN_STARTS_IN_SOLSYS = 1;
	immutable uint MAX_STARTS_IN_SOLSYS = 2;

	/**
	* Aszteroida mező előfordulási valószínűsége egy szektorban.
	* Ha ez nem, akkor köd lesz.
	*/
	immutable float ASTEROID_BELT_PROBABILITY = 0.7;

	immutable float SPECIES_RATES[Universum.Species.max+1] = [
		Universum.Species.SP_NO			: 0.25
			, Universum.Species.SP_HUMAN	: 0.2
			, Universum.Species.SP_MANDULIN	: 0.2
			, Universum.Species.SP_CATOV	: 0.2
			, Universum.Species.SP_HYDROZ	: 0.1
			, Universum.Species.SP_KLOUDER	: 0.05
	];

	immutable float STARTYPES_RATES[Universum.StarTypes.max+1] = [
		Universum.StarTypes.ST_PROTO		: 0.1
			, Universum.StarTypes.ST_MAINSEQ	: 0.7
			, Universum.StarTypes.ST_REDGIANT	: 0.1
			, Universum.StarTypes.ST_WHITEDWARF	: 0.05
			, Universum.StarTypes.ST_NEUTRON	: 0.05
	];

	immutable float CLOUDTYPES_RATES[Universum.CloudTypes.max+1] = [
		Universum.CloudTypes.CT_EMISSION	: 0.45
			, Universum.CloudTypes.CT_PLANETARY	: 0.45
			, Universum.CloudTypes.CT_PROTO		: 0.09
			, Universum.CloudTypes.CT_SUPER		: 0.01
	];

	immutable uint MAX_PLANETS_1STAR_SOLSYS = 8;
	immutable uint MAX_MOONS_PERPLANET_1STAR_SOLSYS = 2;
	immutable uint MAX_SPACESTATIONS_PERPLANET_1STAR_SOLSYS = 2;

	immutable uint MAX_PLANETS_2STAR_SOLSYS = 4;
	immutable uint MAX_SPACESTATIONS_PERPLANET_2STAR_SOLSYS = 2;

	progressdelegate pd;
	logdelegate ld;

	RandomSource rs;
	Universum ud;

	string[] planetname_list;

	void updateprogress(float p) {
		if(pd) pd(p);
	}

	void updatelog(string msg) {
		if(ld) ld(msg);
	}

	/// random faj, a megadott valószínűségek szerint
	Universum.Species getaSpecies() {
		return cast(Universum.Species)rs.dice(SPECIES_RATES);
	}

	Universum.StarTypes getaStarType() {
		return cast(Universum.StarTypes)rs.dice(STARTYPES_RATES);
	}

	Universum.CloudTypes getaCloudType() {
		return cast(Universum.CloudTypes)rs.dice(CLOUDTYPES_RATES);
	}

	Cloud genCloud(uint x_, uint y_, uint z_, uint i) {
		return new Cloud(genCloudName(x_,y_,z_),getaCloudType());
	}

	AsteroidBelt genAsteroidBelt(uint x_, uint y_, uint z_, uint i) {
		return new AsteroidBelt(genAsteroidBeltName(x_,y_,z_));
	}

	/// egy Solar system generálása
	SolSystem genSolSys(uint x_, uint y_, uint z_) {
		auto ss = new SolSystem(genSolSysName(x_,y_,z_));

		auto starcount = rs.getNextMainRnd(MIN_STARTS_IN_SOLSYS, MAX_STARTS_IN_SOLSYS+1);
		for(auto i = 0; i < starcount; ++i) {
			ss.addStar(genStar(x_,y_,z_, i));
			debug updatelog("s");
		}



		return ss;
	}

	/// egy Solar system-ben egy star generálása
	Star genStar(uint x_, uint y_, uint z_, uint i) {
		return new Star(genStarName(x_,y_,z_,i), getaStarType());
	}

	/// egy sectorban egy black hole generálása
	BlackHole genBlackHole(uint x_, uint y_, uint z_) {
		return new BlackHole(rs.getNextMainRnd(2)>0?true:false, genBlackHoleName(x_,y_,z_));
	}

	string genStarName(uint x_, uint y_, uint z_, uint i) {
		return "Star_" ~ to!string(x_) ~ "_" ~ to!string(y_) ~ "_" ~ to!string(z_)
			~ "_" ~ to!string(i) ~ "_" ~ to!string(rs.getNextMainRnd());
	}

	string genSolSysName(uint x_, uint y_, uint z_) {
		return "SolSys_" ~ to!string(x_) ~ "_" ~ to!string(y_) ~ "_" ~ to!string(z_)
			~ "_" ~ to!string(rs.getNextMainRnd());
	}

	string genSectorName(uint x_, uint y_, uint z_, Universum.Species species) {
		return "Sector_" ~ to!string(x_) ~ "_" ~ to!string(y_) ~ "_" ~ to!string(z_)
			~ "/" ~ to!string(species);
	}

	string genBlackHoleName(uint x_, uint y_, uint z_) {
		return "BlackHole_" ~ to!string(x_) ~ "_" ~ to!string(y_) ~ "_" ~ to!string(z_);
	}

	string genCloudName(uint x_, uint y_, uint z_) {
		return "Cloud_" ~ to!string(x_) ~ "_" ~ to!string(y_) ~ "_" ~ to!string(z_)
			~ "_" ~ to!string(rs.getNextMainRnd());
	}

	string genAsteroidBeltName(uint x_, uint y_, uint z_) {
		return "AsteroidBelt_" ~ to!string(x_) ~ "_" ~ to!string(y_) ~ "_" ~ to!string(z_)
			~ "_" ~ to!string(rs.getNextMainRnd());
	}
};
